﻿#pragma once
#include <QApplication>
#include <QString>
#include <QDebug>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QThread>
#include <QCoreApplication>
#include <QMap>
#include <QDateTime>

namespace flow
{
class QDbConnectionPool
{
public:
    inline static QString dbPath = R"(EprDataBase.db)";

    static QSqlQuery query() { return (QSqlQuery(openConnection())); }

    /**
     * @brief 获取数据库连接，连接使用完后不需要手动关闭，数据库连接池会在使用此连接的线程结束后自动关闭连接。
     * 传入的连接名 connectionName 默认为空 (内部会为连接名基于线程的信息创建一个唯一的前缀)，
     * 如果同一个线程需要使用多个不同的数据库连接，可以传入不同的 connectionName
     *
     * @param connectionName 连接的名字
     * @return 返回数据库连接
     */
    static QSqlDatabase openConnection(const QString& connectionName = QString())
    {
        /*
         * 1. 创建连接的全名: 基于线程的地址和传入进来的 connectionName，因为同一个线程可能申请创建多个数据库连接
         * 2. 如果连接已经存在，复用它，而不是重新创建
         * 2.1 返回连接前访问数据库，如果连接断开，可以重新建立连接 (测试: 关闭数据库几分钟后再启动，再次访问数据库)
         * 3. 如果连接不存在，则创建连接
         * 4. 线程结束时，释放在此线程中创建的数据库连接
         * [1] 创建连接的全名: 基于线程的地址和传入进来的 connectionName，因为同一个线程可能申请创建多个数据库连接
         */
        QString baseConnectionName = dbPath + "conn_" + QString::number(quint64(QThread::currentThread()), 16);
        QString fullConnectionName = baseConnectionName + connectionName;
        /* [1.1] 自动释放 */
        _aliveTime[fullConnectionName] = QDateTime::currentSecsSinceEpoch();
        recycleConnection();
        if (QSqlDatabase::contains(fullConnectionName)) {
            /* [2] 如果连接已经存在，复用它，而不是重新创建 */
            QSqlDatabase existingDb = QSqlDatabase::database(fullConnectionName);
            /* [2.1] 返回连接前访问数据库，如果连接断开，可以重新建立连接 (测试: 关闭数据库几分钟后再启动，再次访问数据库) */
            QSqlQuery query("SELECT 1", existingDb);
            if (query.lastError().type() != QSqlError::NoError && !existingDb.open()) {
                qDebug().noquote() << "eprorm### Open datatbase error:" << existingDb.lastError().text();
                return (QSqlDatabase());
            }
            return (existingDb);
        }
        else {
            /* [3] 如果连接不存在，则创建连接 */
            if (qApp != nullptr) {
                /* [4] 线程结束时，释放在此线程中创建的数据库连接 */
                QObject::connect(QThread::currentThread(), &QThread::finished, qApp, [fullConnectionName] {
                    if (QSqlDatabase::contains(fullConnectionName)) {
                        QSqlDatabase::removeDatabase(fullConnectionName);
                        qDebug().noquote() << QString("eprorm### Connection deleted: %1").arg(fullConnectionName);
                    }
                });
            }
            return (createConnection(fullConnectionName));
        }
    }

    /* 创建数据库连接 */
    static QSqlDatabase createConnection(const QString& connectionName, const QString& dbFilename = "")
    {
        static int sn = 0;
        /* 创建一个新的数据库连接 */
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
        /* db.setHostName("localhost"); */
        db.setDatabaseName(dbFilename.isEmpty() ? dbPath : dbFilename);
        /*
         * db.setUserName("root");
         * db.setPassword("root");
         */
        if (db.open()) {
            qDebug().noquote() << QString("eprorm### Connection created: %1, sn: %2").arg(connectionName).arg(++sn);
            return (db);
        }
        qDebug().noquote() << "eprorm### Create connection error:" << db.lastError().text();
        return {};
    }

    /* 判断连接活动时间, 这里可以用定时器 */
    static void recycleConnection()
    {
        for (auto& fullConnectionName : _aliveTime.keys()) {
            qint64 duration = _aliveTime[fullConnectionName] - QDateTime::currentSecsSinceEpoch();
            /* 1h = 3600 */
            if (duration >= 3600) {
                if (QSqlDatabase::contains(fullConnectionName)) {
                    QSqlDatabase::removeDatabase(fullConnectionName);
                    qDebug().noquote() << QString("eprorm### Connection deleted: %1 sleep time: %2h").arg(fullConnectionName).arg(duration / 3600.0);
                }
            }
        }
    }

private:
    /* 记录启动的时间 */
    inline static QMap<QString, qint64> _aliveTime;
};
} // namespace epr
